#include "include/hal_serial.h"
#include <iostream>
#include <mutex>
#include <unistd.h>

HAL::Serial::Serial(const std::string &dev, speed_t baud, DataBits dataBits, Parity parity, StopBits stopBits)
{
    this->dev = dev;
    this->fd = open(this->dev.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
    if (this->fd < 0)
    {
        std::cerr << "Serial: open " << dev << " failed" << std::endl;
        return;
    }
    struct termios opt;
    tcgetattr(this->fd, &opt);
    // 设置为原始模式(纯净的串口操作)
    cfmakeraw(&opt);   
    //清空串口接收缓冲区
    tcflush(this->fd, TCIOFLUSH);

    //设置串口输出波特率
    cfsetospeed(&opt, baud);
    //设置串口输入波特率
    cfsetispeed(&opt, baud);

    //设置数据位数  8
    opt.c_cflag &= ~CSIZE;
    //根据dataBits设置数据位数
    switch (dataBits)
    {
    case DataBits::Five:
        opt.c_cflag |= CS5;
        break;
    case DataBits::Six:
        opt.c_cflag |= CS6;
        break;
    case DataBits::Seven:
        opt.c_cflag |= CS7;
        break;
    case DataBits::Eight:
        opt.c_cflag |= CS8;
        break;
    default:
        break;
    }
    //根据parity设置校验位
    switch (parity)
    {
    case Parity::None:
        opt.c_cflag &= ~PARENB;
        opt.c_iflag &= ~INPCK;
        break;
    case Parity::Odd:
        opt.c_cflag |= PARENB;
        opt.c_cflag |= PARODD;
        opt.c_iflag |= INPCK;
        break;
    case Parity::Even:
        opt.c_cflag |= PARENB;
        opt.c_cflag &= ~PARODD;
        opt.c_iflag |= INPCK;
        break;
    default:
        break;
    }
    //根据stopBits设置停止位
    switch (stopBits)
    {
    case StopBits::One:
        opt.c_cflag &= ~CSTOPB;
        break;
    case StopBits::Two:
        opt.c_cflag |= CSTOPB;
        break;
    default:
        break;
    }
    // 开启接收，使程序不占用控制终端
    opt.c_cflag |= (CLOCAL | CREAD);
    opt.c_cc[VTIME] = 1; // 读取超时1*0.1s
    opt.c_cc[VMIN] = 0;  // 最小读取字节数


    //更新配置
    tcsetattr(this->fd, TCSANOW, &opt);
}

HAL::Serial::~Serial()
{
    if (this->fd >= 0)
    {
        close(this->fd);
    }
}

int HAL::Serial::ReadBuffer(uint8_t *data, size_t len, std::chrono::milliseconds timeout)
{
    std::lock_guard<std::mutex> lock(this->mtx);
    if (this->fd < 0)
    {
        std::cerr << "Serial: not open" << std::endl;
        return -1;
    }

    // 设置select超时
    fd_set readfds;
    FD_ZERO(&readfds);
    FD_SET(this->fd, &readfds);

    struct timeval tv;
    tv.tv_sec = timeout.count() / 1000;
    tv.tv_usec = (timeout.count() % 1000) * 1000;

    // 等待数据到达或超时
    int sret = select(this->fd + 1, &readfds, nullptr, nullptr, &tv);
    if (sret < 0) {
        std::cerr << "Serial: select error" << std::endl;
        return -1;
    } else if (sret == 0) {
        std::cerr << "Serial: read timeout" << std::endl;
        return -1;
    }

    // 有数据到达，读取数据
    int ret = read(this->fd, data, len);
    if (ret < 0)
    {
        std::cerr << "Serial: read " << dev << " failed" << std::endl;
    }
    return ret;
}
int HAL::Serial::WriteBuffer(uint8_t *data, size_t len)
{
    std::lock_guard<std::mutex> lock(this->mtx);
    if (this->fd < 0)
    {
        std::cerr << "Serial: not open" << std::endl;
        return -1;
    }

    tcflush(this->fd, TCOFLUSH); // 只清输出缓冲区


    int ret = write(this->fd, data, len);
    if (ret < 0)
    {
        std::cerr << "Serial: write " << dev << " failed" << std::endl;
    }
    return ret;
}
